﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using xbd.DataConvertLib;

namespace xbd.CommunicationLib
{
    /// <summary>
    ///MelsecMc通信协议的Qna-3E帧 
    /// </summary>
    public class MelsecMcBinary : TCPBase
    {
        /// <summary>
        /// 构造方法，初始化大小端
        /// </summary>
        /// <param name="dataFormat"></param>
        public MelsecMcBinary(DataFormat dataFormat = DataFormat.DCBA)
        {
            DataFormat = dataFormat;
        }

        public DataFormat DataFormat { get; set; }
        /// <summary>
        /// 网络编号
        /// </summary>
        public byte NetWorkNo { get; set; } = 0x00;
        /// <summary>
        /// 请求目标模块编号
        /// </summary>
        public byte DestModuleNo { get; set; } = 0x00;

        /// <summary>
        /// 批量字读取
        /// </summary>
        /// <param name="address"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public OperateResult<byte[]> ReadByteArry(string address, ushort length)
        {
            //拼接报文
            var command = BuildReadMessageFrame(address, length);
            if (!command.IsSuccess) return command;

            //发送接受报文
            var receive = SendAndReceive(command.Content);

            if (receive.IsSuccess) return receive;

            if (receive.Content.Length > 10)
            {
                ushort endCode = BitConverter.ToUInt16(receive.Content, 9);
                if (endCode != 0)
                {
                    return new OperateResult<byte[]>()
                    {
                        IsSuccess = false,
                        ErrorCode = endCode
                    };
                }
                else
                {
                    //解析报文
                    return AnalysisResponseMessage(receive.Content, command.Content[13]==1);
                }
            }
            else
            {
                return OperateResult.CreateFailResult<byte[]>("返回报文长度不够解析:" + BitConverter.ToString(receive.Content));
            }


        }

        /// <summary>
        /// 批量位读取
        /// </summary>
        /// <param name="address"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public OperateResult<bool[]> ReadBoolArry(string address, ushort length)
        {
            var analysis = IsVaildBitAddress(address);  //是否为布尔地址

            if (!analysis.IsSuccess) return OperateResult.CreateFailResult<bool[]>(analysis);

            //是否为有效位地址
            if (analysis.Content)
            {
                var read = ReadByteArry(address, length);

                if (!read.IsSuccess) return OperateResult.CreateFailResult<bool[]>(read);

                return OperateResult.CreateSuccessResult(read.Content.Select(c => c == 0x01).Take(length).ToArray());
            }
            else
            {
                return OperateResult.CreateFailResult<bool[]>("变量地址不是有效位地址");
            }
        }
        /// <summary>
        /// 批量写入
        /// </summary>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public OperateResult WriteByteArry(string address, byte[] value)
        {
            //拼接报文
            var command = BuildWriteMessageFrame(address, value);

            if (!command.IsSuccess) return command;

            //发送并接收
            var receive = SendAndReceive(command.Content);

            //验证报文
            if (receive.Content.Length > 10)
            {
                ushort endCode = BitConverter.ToUInt16(value, 9);
                if (endCode != 0)
                {
                    return new OperateResult()
                    {
                        ErrorCode = endCode,
                        IsSuccess = false
                    };
                }
                return OperateResult.CreateSuccessResult();
            }
            else
            {
                return OperateResult.CreateFailResult("返回报文长度不够解析:" + BitConverter.ToString(receive.Content));
            }
        }
        /// <summary>
        /// 批量布尔写入
        /// </summary>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public OperateResult WriteBoolArry(string address, bool[] value)
        {
              var analysis=IsVaildBitAddress(address);
            if (!analysis.IsSuccess)return analysis;

            //是否为有效位地址
            if (analysis.Content)
            {
                var read = WriteByteArry(address, value.Select(c=>c?(byte)0x01:(byte)0x00).ToArray());

                if (!read.IsSuccess) return read;

                return OperateResult.CreateSuccessResult();
            }
            else
            {
                return OperateResult.CreateFailResult("变量地址不是有效位地址");
            }
        }

        /// <summary>
        /// 根据读取地址和长度，拼接要发送的报文
        /// </summary>
        /// <param name="address"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private OperateResult<byte[]> BuildReadMessageFrame(string address, ushort length)
        {
            //D100=>D+100
            var result = MelsecAddressAnalysis(address);

            if (result.IsSuccess == false)
            {
                return OperateResult.CreateFailResult<byte[]>(result);

            }
            ByteArray sendCommand = new ByteArray();

            //副头部
            sendCommand.Add(0x50, 0x00);
            //网络编号
            sendCommand.Add(NetWorkNo);
            //可编程控制器编号
            sendCommand.Add(0xFF);
            //请求模块的IO编号
            sendCommand.Add(0xFF, 0X03);
            //请求模块站号
            sendCommand.Add(DestModuleNo);
            //请求数据长度
            sendCommand.Add(0x0C, 0X00);
            //CPU监视定时器    0X0A*250ms
            sendCommand.Add(0x0A, 0X00);
            //指令
            sendCommand.Add(0x01, 0x04);
            //子指令
            sendCommand.Add(result.Content1.AreaType, 0x00);
            //起始软元件
            byte[] start = BitConverter.GetBytes(result.Content2);
            sendCommand.Add(start[0], start[1], start[2]);
            //软元件代码
            sendCommand.Add(result.Content1.AreaBinaryCode);
            //软元件点数
            sendCommand.Add((byte)(length % 256), (byte)(length / 256));

            return OperateResult.CreateSuccessResult(sendCommand.array);
        }

        /// <summary>
        /// 根据读取地址的长度和写入的值，拼接要发送的报文
        /// </summary>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private OperateResult<byte[]> BuildWriteMessageFrame(string address, byte[] value)
        {
            var result = MelsecAddressAnalysis(address);
            if (result.IsSuccess == false)
            {
                return OperateResult.CreateFailResult<byte[]>(result);
            }

            //处理byte[] value
            byte[] writeValue = null;

            if (result.Content1.AreaType == 0x01)
            {
                int realLength = value.Length % 2 == 0 ? value.Length / 2 : value.Length / 2 + 1;
                List<byte> data = new List<byte>();

                for (int i = 0; i < realLength; i++)
                {
                    byte b = 0;
                    b += value[2 * i] == 0x01 ? (byte)0x10 : (byte)0x00;
                    //针对计数情况
                    if ((2 * i + 1) >= value.Length)
                    {
                        b += 0x00;
                    }
                    else
                    {
                        b += value[2 * i + 1] == 0x01 ? (byte)0x10 : (byte)0x00;
                    }
                    data.Add(b);
                }
                writeValue = data.ToArray();
            }
            else
            {
                if (value.Length % 2 != 0)
                {
                    return OperateResult.CreateFailResult<byte[]>("写入字节数量必须为偶数");
                }
                else
                {
                    writeValue = value;
                }
            }

            //拼接报文
            ByteArray sendCommand = new ByteArray();

            //副头部
            sendCommand.Add(0x50, 0x00);
            //网络编号
            sendCommand.Add(NetWorkNo);
            //PLC编号
            sendCommand.Add(0xFF);
            //请求目标模块IO编号
            sendCommand.Add(0xFF, 0X03);
            //请求目标模块站号
            sendCommand.Add(DestModuleNo);
            //请求数据长度
            sendCommand.Add((byte)((writeValue.Length + 12) % 256), (byte)((writeValue.Length + 12) / 256));
            //CPU定时器
            sendCommand.Add(0x0A, 0X00);
            //指令
            sendCommand.Add(0x01, 0x14);
            //子指令
            sendCommand.Add(result.Content1.AreaType, 0x00);

            //起始软元件
            byte[] start = BitConverter.GetBytes(result.Content2);
            sendCommand.Add(start[0], start[1], start[2]);
            //软元件代号
            sendCommand.Add(result.Content1.AreaBinaryCode);
            //软元件点数
            if (result.Content1.AreaType == 0x01)
            {
                sendCommand.Add((byte)(value.Length % 256), (byte)(value.Length / 256));
            }
            else
            {
                sendCommand.Add((byte)(value.Length / 2 % 256), (byte)(value.Length / 2 / 256));
            }
            //软元件数据
            sendCommand.Add(writeValue);

            return OperateResult.CreateSuccessResult(sendCommand.array);
        }

        /// <summary>
        /// 解析三菱PLC地址
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        private OperateResult<MelsecMcStoreArea, int> MelsecAddressAnalysis(string address)
        {
            var result = new OperateResult<MelsecMcStoreArea, int>();
            address = address.ToUpper();

            try
            {
                switch (address[0].ToString())
                {
                    case "X":
                        result.Content1 = MelsecMcStoreArea.X;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.X.FromBase);
                        break;
                    case "Y":
                        result.Content1 = MelsecMcStoreArea.Y;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.Y.FromBase);
                        break;
                    case "M":
                        result.Content1 = MelsecMcStoreArea.M;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.M.FromBase);
                        break;
                    case "L":
                        result.Content1 = MelsecMcStoreArea.L;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.L.FromBase);
                        break;
                    case "F":
                        result.Content1 = MelsecMcStoreArea.F;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.F.FromBase);
                        break;
                    case "V":
                        result.Content1 = MelsecMcStoreArea.V;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.V.FromBase);
                        break;
                    case "B":
                        result.Content1 = MelsecMcStoreArea.B;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.B.FromBase);
                        break;
                    case "D":
                        result.Content1 = MelsecMcStoreArea.D;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.D.FromBase);
                        break;
                    case "W":
                        result.Content1 = MelsecMcStoreArea.W;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.W.FromBase);
                        break;
                    case "Z":
                        result.Content1 = MelsecMcStoreArea.Z;
                        result.Content2 = Convert.ToInt32(address.Substring(1), MelsecMcStoreArea.Z.FromBase);
                        break;
                    case "T":
                        result.Content1 = MelsecMcStoreArea.TN;
                        result.Content2 = Convert.ToInt32(address.Substring(2), MelsecMcStoreArea.TN.FromBase);
                        break;
                    case "S":
                        result.Content1 = MelsecMcStoreArea.SN;
                        result.Content2 = Convert.ToInt32(address.Substring(2), MelsecMcStoreArea.SN.FromBase);
                        break;
                    case "CN":
                        result.Content1 = MelsecMcStoreArea.CN;
                        result.Content2 = Convert.ToInt32(address.Substring(2), MelsecMcStoreArea.CN.FromBase);
                        break;
                    default:
                        result.IsSuccess = false;
                        result.Message = "非有效地址:" + address;
                        break;
                }
            }
            catch (Exception ex)
            {
                result.IsSuccess = false;
                result.Message = "地址转换错误:" + ex.Message;
            }

            result.IsSuccess = true;
            result.Message = "Success";
            return result;
        }

        private OperateResult<byte[]> AnalysisResponseMessage(byte[] response, bool isBit)
        {
            byte[] content = ByteArrayLib.GetByteArrayFromByteArray(response, 11);

            //如果是位读取
            if (isBit)
            {
                List<byte> data = new List<byte>();

                for (int i = 0; i < content.Length; i++)
                {
                    //0x10 0x00 0x01
                    data.Add((content[i] & 0x10) == 0x10 ? (byte)0x01 : (byte)0x00);
                    data.Add((content[i] & 0x01) == 0x01 ? (byte)0x01 : (byte)0x00);
                }
                return OperateResult.CreateSuccessResult(data.ToArray());
            }
            else
            {
                return OperateResult.CreateSuccessResult(content);
            }
        }

        /// <summary>
        /// 是否为有效位地址
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        private OperateResult<bool> IsVaildBitAddress(string address)
        {
            var analysis = MelsecAddressAnalysis(address);

            if (!analysis.IsSuccess) return OperateResult.CreateFailResult<bool>(analysis);

            return OperateResult.CreateSuccessResult(analysis.Content1.AreaType == 0x01);
        }
    }

    public class MelsecMcStoreArea
    {
        public MelsecMcStoreArea(byte areaBinaryCode, string areaASCIICode, byte areaType, int fromBase)
        {
            AreaBinaryCode = areaBinaryCode;
            AreaASCIICode = areaASCIICode;
            AreaType = areaType;
            FromBase = fromBase;
        }

        /// <summary>
        /// 存储区二进制代号
        /// </summary>
        public byte AreaBinaryCode { get; set; }
        /// <summary>
        /// 存储区ASCII代号
        /// </summary>
        public string AreaASCIICode { get; set; }
        /// <summary>
        /// 存储区类型：0表示字存储区，1表示位存储区
        /// </summary>
        public byte AreaType { get; set; }
        /// <summary>
        /// 存储区进制
        /// </summary>
        public int FromBase { get; set; }

        /// <summary>
        /// X输入继电器
        /// </summary>
        public readonly static MelsecMcStoreArea X = new MelsecMcStoreArea(0x9C, "X*", 0x01, 16);

        /// <summary>
        /// Y输出继电器
        /// </summary>
        public readonly static MelsecMcStoreArea Y = new MelsecMcStoreArea(0x9D, "Y*", 0x01, 16);

        /// <summary>
        /// M内部继电器
        /// </summary>
        public readonly static MelsecMcStoreArea M = new MelsecMcStoreArea(0x90, "M*", 0x01, 10);

        /// <summary>
        /// L锁存继电器
        /// </summary>
        public readonly static MelsecMcStoreArea L = new MelsecMcStoreArea(0x92, "L*", 0x01, 10);

        /// <summary>
        /// F报警继电器
        /// </summary>
        public readonly static MelsecMcStoreArea F = new MelsecMcStoreArea(0x93, "F*", 0x01, 10);

        /// <summary>
        /// V变址寄存器
        /// </summary>
        public readonly static MelsecMcStoreArea V = new MelsecMcStoreArea(0x94, "V*", 0x01, 10);

        /// <summary>
        /// B链接继电器
        /// </summary>
        public readonly static MelsecMcStoreArea B = new MelsecMcStoreArea(0xA0, "B*", 0x01, 16);

        /// <summary>
        /// D数据寄存器
        /// </summary>
        public readonly static MelsecMcStoreArea D = new MelsecMcStoreArea(0xA8, "D*", 0x00, 10);

        /// <summary>
        /// W链接寄存器
        /// </summary>
        public readonly static MelsecMcStoreArea W = new MelsecMcStoreArea(0xB4, "W*", 0x00, 16);

        /// <summary>
        /// Z变址寄存器
        /// </summary>
        public readonly static MelsecMcStoreArea Z = new MelsecMcStoreArea(0xCC, "Z*", 0x00, 10);

        /// <summary>
        /// TN定时器当前值
        /// </summary>
        public readonly static MelsecMcStoreArea TN = new MelsecMcStoreArea(0xC2, "TN", 0x00, 10);

        /// <summary>
        /// SN累计定时器当前值
        /// </summary>
        public readonly static MelsecMcStoreArea SN = new MelsecMcStoreArea(0xC8, "SN", 0x00, 10);
        /// <summary>
        /// CN计数器当前值
        /// </summary>
        public readonly static MelsecMcStoreArea CN = new MelsecMcStoreArea(0xC5, "CN", 0x00, 10);
    }
}
